using System;

using OR.Lazarus.Business;
using OR.Lazarus.Library;
using Source = OR.Lazarus.Library.DataTier.Source;
using OR.Lazarus.Business.Entities;

namespace OR.Lazarus.Library.DataTier
{
	/// <summary>
	/// Summary description for SourceValidator.
	/// </summary>
   public class SourceValidator : ProjectValidatorBase<Source.ProjectDetail>
	{
		protected override void OnValidate()
		{
			this.OnValidatePK();
			this.OnValidateFK();
			this.OnValidateIntersections();
         this.OnValidateImplements();
		}

		/// <summary>
		/// Ensures that each table has a single PK field, or one is defined under defaults.
		/// </summary>
		protected virtual void OnValidatePK()
		{
			Source.FieldDetail	v_oDefaultPKField;
			Source.FieldDetail	v_oObjectPKField;

			v_oDefaultPKField	= mProject.DefaultSetting.Fields.GetPKField();

			foreach ( Source.ObjectDetail v_oObject in mProject.Objects )
			{
				if ( v_oObject.DBTable == v_oObject.Name )
				{
					v_oObjectPKField = v_oObject.Fields.GetPKField();

					// Exception if both are null.
					if ( ( v_oDefaultPKField == null ) && ( v_oObjectPKField == null ) )
					{
						throw new Exception( "The object [" + v_oObject.Name + "] does not specify a primary key." );
					}

					// Exception if both are not null.
					if ( ( v_oDefaultPKField != null ) && ( v_oObjectPKField != null ) )
					{
						throw new Exception( "The object [" + v_oObject.Name + "] specifies a primary key, but there is already one defined in the Default Fields." );
					}
				}
			}
		}

		/// <summary>
		/// Ensures that each Foreign Key reference refers to a valid object.
		/// </summary>
		protected virtual void OnValidateFK()
		{
			Source.IndexCollection	v_oFKIndexes	= null;
			Source.ObjectDetail		v_oFKObject		= null;

			foreach ( Source.ObjectDetail v_oObject in mProject.Objects )
			{
				foreach ( Source.FieldDetail v_oField in v_oObject.Fields )
				{
					v_oFKIndexes = v_oField.Indexes.GetByType( "Foreign" );

					foreach ( Source.IndexDetail v_oIndex in v_oFKIndexes )
					{
						v_oFKObject = mProject.Objects[ v_oIndex.Object ];

						if ( v_oFKObject == null )
						{
							throw new Exception( "The index on object field [ " + v_oObject.Name + "." + v_oField.Name + " ] specified a reference to object [ " + v_oIndex.Object + " ], which does not exist." );
						}
					}
				}
			}
		}

		protected virtual void OnValidateIntersections()
		{
			Source.ObjectDetail			v_oRefObject	= null;

			foreach ( Source.IntersectionDetail v_oIntersection in mProject.Intersections )
			{
				foreach ( Source.ReferenceDetail v_oReference in v_oIntersection.References )
				{
					v_oRefObject = mProject.Objects[ v_oReference.Object ];

					if ( v_oRefObject == null )
					{
						throw new Exception( "The intersection reference [ " + v_oIntersection.Name + "." + v_oReference.Name + " ] specified a reference to object [ " + v_oReference.Object + " ], which does not exist." );
					}
				}
			}
		}

      protected virtual void OnValidateImplements()
      {
         Source.ObjectDetail v_oRefObject = null;

         foreach ( Source.ObjectDetail vObject in mProject.Objects )
         {
            foreach ( Source.ImplementDetail v_oImplement in vObject.Implements )
            {
               if ( String.IsNullOrEmpty( v_oImplement.DBMethod ) )
               {
                  throw new Exception( "The implement reference from [ " + vObject.Name + " ] to object [ " + v_oImplement.Ref + " ] specifies the invalid DBMethod [ " + v_oImplement.DBMethod + " ], which is invalid.  Implement DBMethod should be Aggregate or Category." );
               }

               v_oRefObject = mProject.Objects[ v_oImplement.Ref ];

               if ( v_oRefObject == null )
               {
                  throw new Exception( "The implement reference from [ " + vObject.Name + " ] to object [ " + v_oImplement.Ref + " ] does not exist." );
               }
            }
         }
      }

		/// <summary>
		/// Default Constructor
		/// </summary>
		public SourceValidator()
		{
			//
			// TODO: Add constructor logic here
			//
		}
	}
}
